package com.mimo.gateway.filter;

import java.util.Objects;

import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;

import com.mimo.common.result.error.SignatureErrorResult;
import com.mimo.gateway.config.AccessConfig;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * 创建一个基本的过滤器，专门针对验签的动作。
 * 
 * @author Hongyu
 */
@Component
@EnableConfigurationProperties(value = { AccessConfig.class })
public class AccessControlFilter extends AbstractGlobalFilter {

  private static final Logger log = LoggerFactory.getLogger(AccessControlFilter.class);
  private static final SignatureErrorResult SIGNATURE_ERROR_RESULT = new SignatureErrorResult();

  @Autowired
  private AccessConfig config;

  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    String appKey = this.getHttpHeader(exchange, "App-Key");
    String nonce = this.getHttpHeader(exchange, "Nonce");
    String timestamp = this.getHttpHeader(exchange, "Timestamp");
    String signature = this.getHttpHeader(exchange, "Signature");

    boolean isValid = !(StringUtils.isEmpty(appKey) // 非空
        || StringUtils.isEmpty(nonce) // 非空
        || StringUtils.isEmpty(timestamp)// 非空
        || StringUtils.isEmpty(signature)// 非空
        || !Objects.equals(appKey, config.getKey()) // App key
        || !Objects.equals(this.getSign(config.getSecrect(), nonce, timestamp), signature)); // 签名

    if (!isValid) { // 验签失败
      log.error("非法请求被拦截. AppKey:[{}],Nonce:[{}],Timestamp:[{}],Sign:[{}]", appKey, nonce, timestamp, signature);
      exchange.getResponse().setStatusCode(HttpStatus.OK);
      return exchange.getResponse()
          .writeWith(Flux.just(this.getBodyBuffer(exchange.getResponse(), SIGNATURE_ERROR_RESULT)));
    }

    return chain.filter(exchange);
  }

  private String getSign(String secrect, String nonce, String ts) {
    return DigestUtils.sha1Hex(secrect + nonce + ts);
  }

  @Override
  public int getOrder() {
    return BIZ_FILTER_BASIC_ORDER - 10;
  }
}
